package per.mapt.basic.nio;

import java.io.Closeable;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Iterator;

public class NioServer implements Closeable {
    private int port;
    private String address;
    private Selector selector;
    private ServerSocketChannel serverChannel;
    private DataWriter dataWriter;
    private final byte[] recv = "recv:".getBytes(Charset.forName("UTF-8"));

    public NioServer(String address, int port) {
        this.port = port;
        this.address = address;
    }

    public void start() throws IOException {
        selector = Selector.open();
        serverChannel = ServerSocketChannel.open();
        serverChannel.configureBlocking(false);
        serverChannel.bind(new InetSocketAddress(address, port));
        serverChannel.register(selector, SelectionKey.OP_ACCEPT);
        dataWriter = new DataWriter("server");
        dataWriter.start();
        new Thread(this::startListen).start();

    }

    protected void startListen() {
        while (true) {
            try {
                if (selector.select(1000) == 0) {
                    continue;
                }

                for (Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); iterator.hasNext(); ) {
                    SelectionKey key = iterator.next();
                    iterator.remove();
                    if (key.isAcceptable()) {
                        ServerSocketChannel ssChannel = (ServerSocketChannel) key.channel();
                        SocketChannel sc = ssChannel.accept();
                        sc.configureBlocking(false);
                        sc.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                    } else if (key.isReadable()) {
                        SocketChannel sc = (SocketChannel) key.channel();
                        ByteBuffer buf = (ByteBuffer) key.attachment();
                        long bytesRead = sc.read(buf);
                        long readLen = 0;
                        while (bytesRead > 0) {
                            buf.flip();
                            while (buf.hasRemaining()) {
                                char c = (char) buf.get();
                                if(c=='\n'){
                                    byte[] readLength = Long.toString(bytesRead).getBytes(Charset.forName("Utf-8"));
                                    byte[] bytes = Arrays.copyOf(recv, recv.length
                                            + readLength.length);
                                    System.arraycopy(readLength, 0, bytes, recv.length, readLength.length);
                                    try {
                                        Thread.sleep(100);
                                    } catch (InterruptedException e) {
                                        e.printStackTrace();
                                    }
                                    dataWriter.writeData(sc, ByteBuffer.wrap(bytes, bytes.length, 0));
                                }
                                System.out.print(c);
                            }
                            readLen += bytesRead;
                            System.out.println();
                            buf.clear();
                            bytesRead = sc.read(buf);
                        }

                        if (bytesRead == -1) {
                            sc.shutdownInput();
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
                break;
            }
        }
    }

    @Override
    public void close() {
        selector.keys().forEach((selectionKey) -> {
            SelectableChannel channel = selectionKey.channel();
            if (channel.isOpen()) {
                try {
                    channel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        try {
            dataWriter.close();
            selector.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
